// Copyright 2013 Ronald Schlenker, www.technewlogic.de.
// 
// This file is part of Byond.
// 
// Byond is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
// 
// Byond is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Byond.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using Byond.Frontend.EnsembleBrowserNew;
using Byond.Frontend.EnsembleBrowserNew.ViewModels;
using Byond.Frontend.Messages;
using Byond.Frontend.OnlineDebugging.ViewModels;
using Byond.Frontend.Views;
using Byond.HostObjects;
using Byond.Messages;
using Byond.Persistence;
using Microsoft.Win32;
using Mvvm;
using Utils.DialogManagement;
using Utils.Messaging;

namespace Byond.Frontend.ViewModels
{
	internal class CodeEditorViewModel : ViewModelBase
	{
		private readonly IDocumentManager _documentManager;
		private readonly Func<DocumentModel, EnsembleEditorViewModel> _ensembleEditorFactory;
		private readonly RuntimeScope _runtimeScope;
		private readonly IEnsembleRunner _ensembleRunner;
		private readonly PersistenceManager _persistenceManager;
		private readonly OpenFileDialog _openSessionDialog;
		private readonly SaveFileDialog _saveSessionDialog;
		private readonly IDialogManager _dialogManager;
		private readonly CachedDocumentEditorHost _cachedDocumentEditorHost;

		public CodeEditorViewModel(
			IDocumentManager documentManager,
			BrowserViewModel ensembleBrowser,
			DebugEditorViewModel debugEditor,
			ModifiedDocumentsViewModel modifiedDocuments,
			MenuItemRegistrar menuItemRegistrar,
			Env env)
		{
			_documentManager = documentManager;
			EnsembleBrowser = ensembleBrowser;
			DebugEditor = debugEditor;
			ModifiedDocuments = modifiedDocuments;

			EnsembleBrowser.PropertyChanged += (s, e) => InvokePropertiesChanged();
			_documentManager.DirtyDocumentsChanged += InvokePropertiesChanged;
			_documentManager.DocumentDiscarded += DocumentDiscarded;

			// Wenn das nicht gemacht wird, wird das DataTemplate im ItemsControl ignoriert.
			MenuItems = menuItemRegistrar.MenuItems
				.Select(it => new { UiElement = it() })
				.ToList();

			_openSessionDialog = new OpenFileDialog
				{
					InitialDirectory = env.PatchPath,
					Multiselect = false,
					DefaultExt = ".session",
					Filter = "Byond Sessions (.session)|*.session"
				};

			_saveSessionDialog = new SaveFileDialog
				{
					InitialDirectory = env.PatchPath,
					DefaultExt = ".session",
					Filter = "Byond Sessions (.session)|*.session"
				};
		}

		private string _currentSelectedPatchFileName;

		public event MessageEventHandler SendMessage;

		public BrowserViewModel EnsembleBrowser { get; private set; }

		public CachedDocumentEditorHost CachedDocumentView { get { return _cachedDocumentEditorHost; } }

		public DebugEditorViewModel DebugEditor { get; private set; }

		public ModifiedDocumentsViewModel ModifiedDocuments { get; private set; }

		public Visibility EnsembleEditorVisibility { get; private set; }
		public Visibility LiveEditorVisibility { get; private set; }
		public Visibility LogVisibility { get; private set; }

		public bool IsInCodeMode { get { return EnsembleEditorVisibility == Visibility.Visible; } }
		public bool IsInLiveMode { get { return LiveEditorVisibility == Visibility.Visible; } }
		public bool IsInDebugMode { get { return LogVisibility == Visibility.Visible; } }

		public IEnumerable<object> MenuItems { get; private set; }

		public bool CanSaveAll { get { return _documentManager.DirtyDocuments.Any(); } }
		public bool CanRun { get { return !string.IsNullOrWhiteSpace(_currentSelectedPatchFileName); } }
		public bool CanStop { get { return _runtimeScope.Current.PatchManager.RootPatch != Patch.Empty; } }
		public bool CanGotoLive { get { return CanStop; } }
		public bool CanSaveSession { get { return CanStop; } }

		public string ByondReleaseInfo
		{
			get
			{
				return "BYOND (C) Ronald Schlenker, Build " + GetType().Assembly.GetName().Version + ", " + File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location);
			}
		}

		public void HandleMessage(RunEnsembleRequest message)
		{
			if (CanRun)
				Run();
		}

		public void HandleMessage(EditDocumentRequest message)
		{
			var fileName = message.FileName;
			ShowDocument(fileName);
		}

		public void HandleMessage(BrowserSelectionChangedMessage message)
		{
			_currentSelectedPatchFileName = message.PatchFileName;
			InvokePropertiesChanged();
		}

		public void HandleMessage(EnsembleStoppedMessage message)
		{
			InvokePropertiesChanged();
		}

		public void HandleMessage(EnsembleRunningMessage message)
		{
			InvokePropertiesChanged();
		}

		public void SaveAll()
		{
			_documentManager.SaveAll();
		}

		public void Run()
		{
			_documentManager.SaveAll();
			_ensembleRunner.Run(_currentSelectedPatchFileName);
		}

		public void Stop()
		{
			_ensembleRunner.Stop();
		}

		public void GotoLive()
		{
			SendMessage(new ShowViewRequest(typeof(LiveView)));
		}

		public void GotoLog()
		{
			SendMessage(new ShowViewRequest(typeof(LogViewModel)));
		}

		public void OpenSession()
		{
			if (!_openSessionDialog.ShowDialog().Value)
				return;
			_persistenceManager.RestoreAndRunEnsemble(File.ReadAllText(_openSessionDialog.FileName));
		}

		public void SaveSession()
		{
			if (_runtimeScope.Current.PatchManager.RootPatch == Patch.Empty)
			{
				_dialogManager
					.CreateMessageDialog(
						"Saving of sessions if only possible for a running patch.",
						"Cannot save session",
						DialogMode.Ok)
					.Show();
				return;
			}

			if (!_saveSessionDialog.ShowDialog().Value)
				return;
			if (string.IsNullOrWhiteSpace(_saveSessionDialog.FileName))
				return;

			var json = _persistenceManager.PersistCurrentStateToJson();
			File.WriteAllText(_saveSessionDialog.FileName, json);
		}

		private void ShowDocument(string fileName)
		{
			var document = _documentManager.TryGetDocumentFor(fileName);
			if (document != null)
			{
				ShowDocument(document);
				InvokePropertiesChanged();
			}
			else
			{
				_dialogManager
					.CreateMessageDialog("Could not open patch.", DialogMode.Ok)
					.Show();
			}
		}

		private void DocumentDiscarded(string fileName)
		{
			if (_cachedDocumentEditorHost.EnsembleEditorViewModel != null
				&& _cachedDocumentEditorHost.EnsembleEditorViewModel.Document.FileName == fileName)
			{
				ShowDocument(_cachedDocumentEditorHost.EnsembleEditorViewModel.Document.FileName);
			}

			InvokePropertiesChanged();
		}

		private void ShowDocument(DocumentModel ensemble)
		{
			var ensembleEditor = ensemble == null
				? null
				: _ensembleEditorFactory(ensemble);
			_cachedDocumentEditorHost.EnsembleEditorViewModel = ensembleEditor;

			OnPropertyChanged("CanGotoCode");
		}

		private void InvokePropertiesChanged()
		{
			OnPropertyChanged("CanSaveAll");
			OnPropertyChanged("CanRun");
			OnPropertyChanged("CanStop");
			OnPropertyChanged("CanSaveSession");
			OnPropertyChanged("CanGotoLive");
			OnPropertyChanged("CanNew");
			OnPropertyChanged("CanSave");
			OnPropertyChanged("CanSaveAs");
		}
	}
}